home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 4: GNU Archives / Linux Cubed Series 4 - GNU Archives.iso / gnu / fontutil.6 / fontutil / fontutils-0.6 / tfm / tfm_input.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-10-19  |  14.1 KB  |  507 lines

  1. /* tfm_input.c: read a TFM file.
  2.  
  3. Copyright (C) 1992 Free Software Foundation, Inc.
  4.  
  5. This program is free software; you can redistribute it and/or modify
  6. it under the terms of the GNU General Public License as published by
  7. the Free Software Foundation; either version 2, or (at your option)
  8. any later version.
  9.  
  10. This program is distributed in the hope that it will be useful,
  11. but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13. GNU General Public License for more details.
  14.  
  15. You should have received a copy of the GNU General Public License
  16. along with this program; if not, write to the Free Software
  17. Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
  18.  
  19. #include "config.h"
  20.  
  21. #include "file-input.h"
  22. #include "tfm.h"
  23.  
  24.  
  25. /* If true, print out what we read.  */
  26. static boolean tracing_tfm_input = false;
  27.  
  28. /* These identify the file we're reading.  */
  29. static FILE *tfm_input_file;
  30. static string tfm_input_name;
  31.  
  32.  
  33. /* A copy of the character information.  We rely on the `exists' member
  34.    of all these characters being initialized to zero (i.e., `false') by
  35.    virtue of the variable being static.  */
  36. static tfm_char_type tfm_char_table[TFM_SIZE];
  37.  
  38. /* A copy of the global information.  */
  39. static tfm_global_info_type *global_info = NULL;
  40.  
  41. /* Global information which is only useful to us, not the user.  The
  42.    `..._pos' members record the location of the various components of
  43.    the file.  The member `param_word_count' says how many words of
  44.    header information are present in this TFM file.  */
  45. typedef struct
  46. {
  47.   byte_count_type char_info_pos;
  48.   byte_count_type width_pos;
  49.   byte_count_type height_pos;
  50.   byte_count_type depth_pos;
  51.   byte_count_type italic_correction_pos;
  52.   byte_count_type lig_kern_pos;
  53.   byte_count_type kern_pos;
  54.   unsigned param_word_count;
  55. } tfm_header_type;
  56.  
  57. static tfm_header_type tfm_header;
  58.  
  59.  
  60. /* Low-level input.  These macros call the corresponding routines in
  61.    kbase, using the static variables for the input file and filename.  */
  62. #define TFM_FTELL() xftell (tfm_input_file, tfm_input_name)
  63. #define TFM_FSEEK(offset, from_where) \
  64.   xfseek (tfm_input_file, offset, from_where, tfm_input_name)
  65. #define TFM_GET_BYTE() get_byte (tfm_input_file, tfm_input_name)
  66. #define TFM_GET_TWO() get_two (tfm_input_file, tfm_input_name)
  67. #define TFM_GET_FOUR() get_four (tfm_input_file, tfm_input_name)
  68.  
  69.  
  70. static tfm_char_type get_char ();
  71. static string tfm_get_bcpl_string (void);
  72. static real tfm_get_fix_word (void);
  73. static void get_lig_kern_program (list_type *, list_type *);
  74. static real tfm_get_scaled_fix (void);
  75. static void get_tfm_header (void);
  76. static void get_tfm_params (void);
  77.  
  78. /* Routines to start and finish reading a file.  (For the user to call.)  */
  79.  
  80. boolean
  81. tfm_open_input_file (string filename)
  82. {
  83.   if (tfm_input_file != NULL)
  84.     FATAL2 ("tfm_open_input_file: Attempt to open `%s', but `%s' is
  85. already open", filename, tfm_input_name);
  86.  
  87.   tfm_input_name = filename;
  88.   tfm_input_file = fopen (filename, "r");
  89.  
  90.   return tfm_input_file != NULL;
  91. }
  92.  
  93.  
  94. void
  95. tfm_close_input_file ()
  96. {
  97.   assert (tfm_input_file != NULL);
  98.  
  99.   xfclose (tfm_input_file, tfm_input_name);
  100.  
  101.   tfm_input_file = NULL;
  102.   tfm_input_name = NULL;
  103.   global_info = NULL;
  104. }
  105.  
  106.  
  107. /* This is just avoids all callers having to keep a global around with
  108.    the TFM name they used.  */
  109.  
  110. string
  111. tfm_input_filename ()
  112. {
  113.   if (tfm_input_file == NULL)
  114.     return NULL;
  115.   
  116.   return tfm_input_name;
  117. }
  118.  
  119. /* Some of the global information comes from the beginning of the file,
  120.    and some from the end.  If we've already read the information from
  121.    the file, we don't do it again.  */
  122.  
  123. tfm_global_info_type
  124. tfm_get_global_info ()
  125. {
  126.   assert (tfm_input_file != NULL);
  127.   
  128.   /* Only read the file once.  */
  129.   if (global_info != NULL) return *global_info; 
  130.   
  131.   global_info = XTALLOC1 (tfm_global_info_type);
  132.   get_tfm_header ();
  133.   get_tfm_params ();
  134.  
  135.   return *global_info;
  136. }
  137.  
  138.  
  139. /* Now comes convenience routines to get particular parts of the global
  140.    info.  */
  141.  
  142. unsigned
  143. tfm_get_checksum ()
  144. {
  145.   (void) tfm_get_global_info ();
  146.   return TFM_CHECKSUM (*global_info);
  147. }
  148.  
  149.  
  150. double
  151. tfm_get_design_size ()
  152. {
  153.   (void) tfm_get_global_info ();
  154.   return TFM_DESIGN_SIZE (*global_info);
  155. }
  156.  
  157.  
  158. string
  159. tfm_get_coding_scheme ()
  160. {
  161.   (void) tfm_get_global_info ();
  162.   return TFM_CODING_SCHEME (*global_info);
  163. }
  164.  
  165.  
  166. double
  167. tfm_get_interword_space ()
  168. {
  169.   (void) tfm_get_global_info ();
  170.   return TFM_FONTDIMEN (*global_info, TFM_SPACE_PARAMETER);
  171. }
  172.  
  173.  
  174. double
  175. tfm_get_x_height ()
  176. {
  177.   (void) tfm_get_global_info ();
  178.   return TFM_FONTDIMEN (*global_info, TFM_XHEIGHT_PARAMETER);
  179. }
  180.  
  181.  
  182. /* Here we read the information at the beginning of the file.  We store
  183.    the result into the static variables `global_info' and
  184.    `tfm_header'.  */
  185.  
  186. static void
  187. get_tfm_header ()
  188. {
  189.   two_bytes file_length, header_length;
  190.   two_bytes width_word_count, height_word_count, depth_word_count;
  191.   two_bytes italic_correction_word_count, lig_kern_word_count;
  192.   two_bytes kern_word_count, extensible_word_count;
  193.  
  194.   /* The header is at the beginning of the file, naturally.  */
  195.   TFM_FSEEK (0, SEEK_SET);
  196.  
  197.   file_length = TFM_GET_TWO ();
  198.   header_length = TFM_GET_TWO ();
  199.  
  200.   global_info->first_charcode = TFM_GET_TWO ();
  201.   global_info->last_charcode = TFM_GET_TWO ();
  202.   width_word_count = TFM_GET_TWO ();
  203.   height_word_count = TFM_GET_TWO ();
  204.   depth_word_count = TFM_GET_TWO ();
  205.   italic_correction_word_count = TFM_GET_TWO ();
  206.   lig_kern_word_count = TFM_GET_TWO ();
  207.   kern_word_count = TFM_GET_TWO ();
  208.   extensible_word_count = TFM_GET_TWO ();
  209.   tfm_header.param_word_count = TFM_GET_TWO ();
  210.   TFM_FONTDIMEN_COUNT (*global_info) = tfm_header.param_word_count;
  211.  
  212.   tfm_header.char_info_pos = (6 + header_length) * 4;
  213.   tfm_header.width_pos = tfm_header.char_info_pos
  214.                          + (global_info->last_charcode
  215.                             - global_info->first_charcode + 1) * 4;
  216.   tfm_header.height_pos = tfm_header.width_pos + width_word_count * 4;
  217.   tfm_header.depth_pos = tfm_header.height_pos + height_word_count * 4;
  218.   tfm_header.italic_correction_pos = tfm_header.depth_pos
  219.                                      + depth_word_count * 4;
  220.   tfm_header.lig_kern_pos = tfm_header.italic_correction_pos
  221.     + italic_correction_word_count * 4;
  222.   tfm_header.kern_pos = tfm_header.lig_kern_pos + lig_kern_word_count * 4;
  223.   /* We don't care about the extensible table.  */
  224.  
  225.   if (header_length < 2)
  226.     FATAL2 ("TFM header of `%s' has only %u word(s)", tfm_input_name,
  227.             header_length);
  228.  
  229.   TFM_CHECKSUM (*global_info) = TFM_GET_FOUR ();
  230.   TFM_DESIGN_SIZE (*global_info) = tfm_get_fix_word ();
  231.  
  232.   /* Although the coding scheme might be interesting to the caller, the
  233.      font family and face byte probably aren't.  So we don't read them.  */
  234.   TFM_CODING_SCHEME (*global_info)
  235.     = header_length > 2 ? tfm_get_bcpl_string () : "unspecified";
  236.  
  237.   if (tracing_tfm_input)
  238.     printf ("TFM checksum = %u, design_size = %fpt, coding scheme = `%s'.\n",
  239.           TFM_CHECKSUM (*global_info),
  240.               TFM_DESIGN_SIZE (*global_info),
  241.           TFM_CODING_SCHEME (*global_info));
  242. }
  243.  
  244.  
  245. /* Although TFM files are only usable by TeX if they have at least seven
  246.    parameters, that is not a requirement of the file format itself, so
  247.    we don't impose it.  And they can have many more than seven, of
  248.    course.  We do impose a limit of TFM_MAX_FONT_PARAMETERS.  We assume
  249.    that `tfm_header' has already been filled in.  */
  250.  
  251. static void
  252. get_tfm_params ()
  253. {
  254.   unsigned this_param;
  255.  
  256.   /* If we have no font parameters at all, we're done.  */
  257.   if (tfm_header.param_word_count == 0)
  258.     return;
  259.  
  260.   /* Move to the beginning of the parameter table in the file.  */
  261.   TFM_FSEEK (-4 * tfm_header.param_word_count, SEEK_END);
  262.  
  263.   /* It's unlikely but possible that this TFM file has more fontdimens
  264.      than we can deal with.  */
  265.   if (tfm_header.param_word_count > TFM_MAX_FONTDIMENS)
  266.     {
  267.       WARNING3 ("%s: TFM file has %u parameters, which is more than the
  268. %u I can handle",
  269.                 tfm_input_name, tfm_header.param_word_count,
  270.                 TFM_MAX_FONTDIMENS);
  271.       tfm_header.param_word_count = TFM_MAX_FONTDIMENS;
  272.     }
  273.  
  274.   /* The first parameter is different than all the rest, because it
  275.      isn't scaled by the design size.  */
  276.   TFM_FONTDIMEN (*global_info, TFM_SLANT_PARAMETER) = tfm_get_fix_word ();
  277.  
  278.   for (this_param = 2; this_param <= tfm_header.param_word_count;
  279.        this_param++)
  280.     TFM_FONTDIMEN (*global_info, this_param) = tfm_get_scaled_fix ();
  281.  
  282.   if (tracing_tfm_input)
  283.     {
  284.       for (this_param = 1; this_param <= tfm_header.param_word_count;
  285.            this_param++)
  286.          printf ("TFM parameter %d: %.3f", this_param,
  287.                  TFM_FONTDIMEN (*global_info, this_param));
  288.     }
  289. }
  290.  
  291. /* Read every character in the TFM file, storing the result in the
  292.    static `tfm_char_table'.  We return a copy of that variable.  */
  293.  
  294. tfm_char_type *
  295. tfm_get_chars ()
  296. {
  297.   tfm_char_type *tfm_chars;
  298.   unsigned this_char;
  299.  
  300.   assert (tfm_input_file != NULL);
  301.  
  302.   tfm_get_global_info ();
  303.  
  304.   for (this_char = global_info->first_charcode;
  305.        this_char <= global_info->last_charcode;
  306.        this_char++)
  307.     /* This fills in the `tfm_char_table' global.  */
  308.     (void) tfm_get_char (this_char);
  309.  
  310.   /* Return a copy, so our information can't get corrupted.  */
  311.   tfm_chars = XTALLOC (TFM_SIZE, tfm_char_type);
  312.   memcpy (tfm_chars, tfm_char_table, sizeof (tfm_char_table));
  313.   return tfm_chars;
  314. }
  315.  
  316.  
  317. /* Read the character CODE.  If the character doesn't exist, return
  318.    NULL.  If it does, save the information in `tfm_char_table', as well
  319.    as returning it.  */
  320.  
  321. tfm_char_type *
  322. tfm_get_char (charcode_type code)
  323. {
  324.   assert (tfm_input_file != NULL);
  325.  
  326.   tfm_get_global_info ();
  327.  
  328.   /* If the character is outside the declared bounds in the file, don't
  329.      try to read it.  Just return NULL.  */
  330.   if (code < global_info->first_charcode
  331.       || code > global_info->last_charcode)
  332.     return NULL;
  333.   
  334.   /* Move to the appropriate place in the `char_info' array.  */
  335.   TFM_FSEEK (tfm_header.char_info_pos
  336.              + (code - global_info->first_charcode) * 4,
  337.          SEEK_SET);
  338.  
  339.   /* Read the character.  */
  340.   tfm_char_table[code] = get_char ();
  341.   
  342.   /* If it exists, return a pointer to it.  We return a copy, so our
  343.      information can't get corrupted.  */
  344.   if (!TFM_CHAR_EXISTS (tfm_char_table[code]))
  345.     return NULL;
  346.   else
  347.     {
  348.       tfm_char_type *c = XTALLOC1 (tfm_char_type);
  349.       
  350.       TFM_CHARCODE (tfm_char_table[code]) = code;
  351.       *c = tfm_char_table[code];
  352.       return c;
  353.     }
  354. }
  355.  
  356.  
  357. /* We assume we are positioned at the beginning of a `char_info' word.
  358.    We read that word to get the indexes into the dimension tables; then
  359.    we go read the tables to get the values (if the character exists).  */
  360.  
  361. static tfm_char_type
  362. get_char ()
  363. {
  364.   one_byte width_index, height_index, depth_index, italic_correction_index;
  365.   one_byte packed;
  366.   one_byte tag, remainder;
  367.   tfm_char_type tfm_char;
  368.  
  369.   /* Read the char_info word.  */
  370.   width_index = TFM_GET_BYTE ();
  371.  
  372.   packed = TFM_GET_BYTE ();
  373.   height_index = (packed & 0xf0) >> 4;
  374.   depth_index = packed & 0x0f;
  375.  
  376.   packed = TFM_GET_BYTE ();
  377.   italic_correction_index = (packed & 0xfc) >> 6;
  378.   tag = packed & 0x3;
  379.  
  380.   remainder = TFM_GET_BYTE ();
  381.  
  382.   tfm_char = tfm_new_char ();
  383.  
  384. #define GET_CHAR_DIMEN(d)                        \
  385.    if (d##_index != 0)                            \
  386.      {                                    \
  387.        TFM_FSEEK (tfm_header.##d##_pos + d##_index*4, SEEK_SET);    \
  388.        tfm_char.fix_##d = TFM_GET_FOUR ();                \
  389.        tfm_char.##d = fix_to_real (tfm_char.fix_##d)            \
  390.                       * global_info->design_size;            \
  391.      }
  392.  
  393.   GET_CHAR_DIMEN (width);
  394.   GET_CHAR_DIMEN (height);
  395.   GET_CHAR_DIMEN (depth);
  396.   GET_CHAR_DIMEN (italic_correction);
  397.  
  398.   /* The other condition for a character existing is that it be between
  399.      the first and last character codes given in the header.  We've
  400.      already assumed that's true (or we couldn't be positioned at a
  401.      `char_info_word').  */
  402.   TFM_CHAR_EXISTS (tfm_char) = width_index != 0;
  403.  
  404.   if (tracing_tfm_input)
  405.     {
  406.       printf ("   width = %f, height = %f, ", tfm_char.width, tfm_char.height);
  407.       printf ("depth = %f, ic = %f.\n", tfm_char.depth,
  408.               tfm_char.italic_correction); 
  409.     }
  410.  
  411.   if (tag == 1)
  412.     {
  413.       TFM_FSEEK (tfm_header.lig_kern_pos + remainder * 4, SEEK_SET);
  414.       get_lig_kern_program (&(tfm_char.ligature), &(tfm_char.kern));
  415.     }
  416.  
  417.   /* We don't handle the other tags.  */
  418.   return tfm_char;
  419. }
  420.  
  421. /* Read a ligature/kern program at the current position, storing the
  422.    result into *LIGATURE and *KERN.  We don't distinguish all the kinds
  423.    of ligatures that Metafont can output.  */
  424.  
  425. #define STOP_FLAG 128
  426. #define KERN_FLAG 128
  427.  
  428. static void
  429. get_lig_kern_program (list_type *ligature, list_type *kern)
  430. {
  431.   boolean end_of_program;
  432.  
  433.   assert (ligature != NULL && kern != NULL);
  434.  
  435.   do
  436.     {
  437.       one_byte next_char;
  438.       boolean kern_step;
  439.       one_byte remainder;
  440.  
  441.       end_of_program = TFM_GET_BYTE () >= STOP_FLAG;
  442.  
  443.       next_char = TFM_GET_BYTE ();
  444.       kern_step = TFM_GET_BYTE () >= KERN_FLAG;
  445.       remainder = TFM_GET_BYTE ();
  446.  
  447.       if (tracing_tfm_input)
  448.       printf ("   if next = %u (%c), ", next_char, next_char);
  449.  
  450.       if (kern_step)
  451.     {
  452.       byte_count_type old_pos = TFM_FTELL ();
  453.       tfm_kern_type *kern_element = LIST_TAPPEND (kern, tfm_kern_type);
  454.  
  455.       kern_element->character = next_char;
  456.  
  457.       TFM_FSEEK (tfm_header.kern_pos + remainder * 4, SEEK_SET);
  458.       kern_element->kern = tfm_get_scaled_fix ();
  459.       TFM_FSEEK (old_pos, SEEK_SET);
  460.  
  461.       if (tracing_tfm_input)
  462.           printf ("kern %f.\n", kern_element->kern);
  463.     }
  464.       else
  465.     {
  466.       tfm_ligature_type *ligature_element
  467.             = LIST_TAPPEND (ligature, tfm_ligature_type);
  468.  
  469.       ligature_element->character = next_char;
  470.       ligature_element->ligature = remainder;
  471.  
  472.       if (tracing_tfm_input)
  473.           printf ("ligature %d (hex %x).\n",
  474.                 ligature_element->ligature, ligature_element->ligature);
  475.     }
  476.   } while (!end_of_program);
  477. }
  478.  
  479. /* Most quantities are fixed-point fractions.  */
  480.  
  481. static real
  482. tfm_get_fix_word ()
  483. {
  484.   return fix_to_real (TFM_GET_FOUR ());
  485. }
  486.  
  487.  
  488. /* Dimensions are a `fix_word' scaled by the design size.  */
  489.  
  490. static real
  491. tfm_get_scaled_fix ()
  492. {
  493.   return tfm_get_fix_word () * global_info->design_size;
  494. }
  495.  
  496.  
  497. static string
  498. tfm_get_bcpl_string ()
  499. {
  500.   unsigned string_length = TFM_GET_BYTE ();
  501.   string s = get_n_bytes (string_length, tfm_input_file, tfm_input_name);
  502.   s = xrealloc (s, string_length + 1);
  503.   s[string_length] = 0;
  504.  
  505.   return s;
  506. }
  507.